package com.example.redisgeo.redisgeo.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Optional;

/**
 * Created by  HanleyTang on 2019-02-27
 */
@Component
public class RedisService {

    static final protected Logger logger = LoggerFactory.getLogger(RedisService.class);

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    private final String geoKey = "gis2";


    @Autowired(required = false)
    public void setRedisTemplate(RedisTemplate redisTemplate) {
        RedisSerializer stringSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringSerializer);
        redisTemplate.setValueSerializer(stringSerializer);
        redisTemplate.setHashKeySerializer(stringSerializer);
        redisTemplate.setHashValueSerializer(stringSerializer);
        this.redisTemplate = redisTemplate;
    }


    /**
     * 普通key,value添加
     *
     * @param key   the key
     * @param value the value
     */
    public void addStr(String key,String value){
        stringRedisTemplate.opsForValue().set(key,value);
    }

    /**
     * RedisGeo 添加坐标
     *
     * @param x    经度
     * @param y    维度
     * @param name 名称(可以是位置信息,设备别名,依业务而定)
     */
    public void add(double x,double y,String name){
        long result = redisTemplate.opsForGeo().add(geoKey, new Point(x, y), name);
        logger.info("RedisService.add successful,x={},y={},name={},result={}",x,y,name,result);
    }


    /**
     * RedisGeo 添加坐标
     *
     * @param key   key
     * @param x     经度
     * @param y     维度
     * @param name  名称(可以是位置信息,设备别名,依业务而定)
     */
    public void add(String key,double x,double y,String name){
        long result = redisTemplate.opsForGeo().add(key, new Point(x, y), name);
        logger.info("RedisService.add successful,key={},x={},y={},name={},result={}",key,x,y,name,result);
    }




    /**
     * 查询Name对应的经纬度
     *
     * @param name 名称
     * @return the list
     */
    public List<Point> query(String ... name){
        List<Point> points = redisTemplate.opsForGeo().position(geoKey,name);
        return points;
    }


    /**
     * 查询Name指定范围数据(附近坐标)
     *
     * @param name        名称
     * @param distanceVal 查询范围(km单位)
     * @param limit       最多返回数据笔数
     * @return the object
     */
    public Object nearByName(String name,int distanceVal,int limit){

        Distance distance = new Distance(distanceVal, Metrics.KILOMETERS);
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs
                .newGeoRadiusArgs().includeDistance().includeCoordinates().sortAscending().limit(limit);

        GeoResults<RedisGeoCommands.GeoLocation<String>> results = redisTemplate.opsForGeo().radius(geoKey,name,distance,args);

        boolean flag = Optional.ofNullable(results).isPresent();
        if (flag){
            return results.getContent();
        }else {
            return null;
        }

    }


    /**
     * 查询特定坐标 指定范围(km单位) 中的数据
     *
     * @param x           经度
     * @param y           维度
     * @param distanceVal 查询范围(km单位)
     * @param limit       最多返回数据笔数
     * @return the object
     */
    public Object nearByCoord(double x,double y,int distanceVal,int limit){

        Distance distance = new Distance(distanceVal, Metrics.KILOMETERS);
        Circle circle = new Circle(new Point(x, y),distance);

        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs
                .newGeoRadiusArgs().includeDistance().includeCoordinates().sortAscending().limit(limit);

        GeoResults<RedisGeoCommands.GeoLocation<String>>  results = redisTemplate.opsForGeo().radius(geoKey,circle,args);

        boolean flag = Optional.ofNullable(results).isPresent();
        if (flag){
            return results.getContent();
        }else {
            return null;
        }

    }


}
